# 版本: 1.0.0
# 作者: 陈振玺
# 功能: 工具模块，包含文件处理、进度条等实用功能

import os
import zipfile
import tempfile
from pathlib import Path
from typing import Optional, Union, List
from tqdm import tqdm
from rich.console import Console
from rich.progress import Progress, TaskID, BarColumn, TextColumn, TimeRemainingColumn
import time


class ProgressBar:
    """进度条工具类"""
    
    def __init__(self, description: str, total: int, unit: str = "bytes"):
        """初始化进度条
        
        Args:
            description: 进度条描述
            total: 总量
            unit: 单位
        """
        self.description = description
        self.total = total
        self.unit = unit
        self.current = 0
        
        # 使用rich创建进度条
        self.progress = Progress(
            TextColumn("[bold blue]{task.description}"),
            BarColumn(),
            "[progress.percentage]{task.percentage:>3.0f}%",
            TextColumn("[bold green]{task.completed}/{task.total}"),
            TimeRemainingColumn(),
            console=Console()
        )
        
        self.task_id = self.progress.add_task(
            self.description, 
            total=total
        )
        
        self.progress.start()
    
    def update(self, advance: int) -> None:
        """更新进度
        
        Args:
            advance: 增加的进度量
        """
        self.current += advance
        self.progress.update(self.task_id, advance=advance)
    
    def set_progress(self, completed: int) -> None:
        """设置当前进度
        
        Args:
            completed: 已完成的量
        """
        advance = completed - self.current
        if advance > 0:
            self.update(advance)
    
    def close(self) -> None:
        """关闭进度条"""
        self.progress.stop()
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()


class FileHandler:
    """文件处理工具类"""
    
    def __init__(self):
        """初始化文件处理器"""
        self.temp_files = []  # 跟踪临时文件
    
    def create_zip_archive(self, 
                          source_path: Union[str, Path], 
                          output_path: Optional[Union[str, Path]] = None,
                          show_progress: bool = True) -> Path:
        """创建ZIP压缩包
        
        Args:
            source_path: 源文件夹路径
            output_path: 输出ZIP文件路径（可选）
            show_progress: 是否显示进度条
            
        Returns:
            ZIP文件路径
            
        Raises:
            Exception: 压缩失败时抛出异常
        """
        source_path = Path(source_path)
        
        if not source_path.exists():
            raise FileNotFoundError(f"源路径不存在: {source_path}")
        
        if not source_path.is_dir():
            raise ValueError(f"源路径不是文件夹: {source_path}")
        
        # 确定输出路径
        if output_path is None:
            # 创建临时文件
            temp_dir = tempfile.gettempdir()
            output_path = Path(temp_dir) / f"{source_path.name}.zip"
            self.temp_files.append(output_path)
        else:
            output_path = Path(output_path)
        
        # 确保输出目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            # 计算总文件数和大小
            files_to_compress = []
            total_size = 0
            
            for root, dirs, files in os.walk(source_path):
                for file in files:
                    file_path = Path(root) / file
                    if file_path.exists():
                        files_to_compress.append(file_path)
                        total_size += file_path.stat().st_size
            
            if not files_to_compress:
                raise ValueError(f"文件夹为空: {source_path}")
            
            # 创建ZIP文件
            with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                if show_progress and total_size > 1024 * 1024:  # 大于1MB显示进度
                    with ProgressBar(f"压缩 {source_path.name}", total_size) as pbar:
                        for file_path in files_to_compress:
                            # 计算相对路径
                            arcname = file_path.relative_to(source_path)
                            
                            # 添加文件到ZIP
                            zipf.write(file_path, arcname)
                            
                            # 更新进度
                            file_size = file_path.stat().st_size
                            pbar.update(file_size)
                else:
                    # 不显示进度条
                    for file_path in files_to_compress:
                        arcname = file_path.relative_to(source_path)
                        zipf.write(file_path, arcname)
            
            return output_path
            
        except Exception as e:
            # 清理失败的文件
            if output_path.exists():
                output_path.unlink()
            raise Exception(f"创建ZIP压缩包失败: {e}")
    
    def get_file_size(self, file_path: Union[str, Path]) -> int:
        """获取文件大小（字节）
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件大小（字节）
            
        Raises:
            FileNotFoundError: 文件不存在时抛出异常
        """
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {file_path}")
        return path.stat().st_size
    
    def get_file_size_str(self, size_bytes: int) -> str:
        """将字节大小转换为可读字符串
        
        Args:
            size_bytes: 字节大小
            
        Returns:
            可读的大小字符串
        """
        if size_bytes == 0:
            return "0B"
        
        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        size = float(size_bytes)
        
        while size >= 1024.0 and i < len(size_names) - 1:
            size /= 1024.0
            i += 1
        
        return f"{size:.1f}{size_names[i]}"
    
    def validate_file_path(self, file_path: Union[str, Path]) -> bool:
        """验证文件路径是否有效
        
        Args:
            file_path: 文件路径
            
        Returns:
            路径是否有效
        """
        try:
            path = Path(file_path)
            return path.exists() and (path.is_file() or path.is_dir())
        except Exception:
            return False
    
    def get_file_info(self, file_path: Union[str, Path]) -> dict:
        """获取文件信息
        
        Args:
            file_path: 文件路径
            
        Returns:
            文件信息字典
        """
        path = Path(file_path)
        
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {path}")
        
        stat = path.stat()
        
        return {
            "name": path.name,
            "path": str(path.absolute()),
            "size": stat.st_size,
            "size_str": self.get_file_size_str(stat.st_size),
            "is_file": path.is_file(),
            "is_dir": path.is_dir(),
            "modified_time": stat.st_mtime,
            "created_time": stat.st_ctime
        }
    
    def read_text_file(self, file_path: Union[str, Path], encoding: str = 'utf-8') -> str:
        """读取文本文件内容
        
        Args:
            file_path: 文件路径
            encoding: 文件编码
            
        Returns:
            文件内容
            
        Raises:
            Exception: 读取失败时抛出异常
        """
        path = Path(file_path)
        
        if not path.exists():
            raise FileNotFoundError(f"文件不存在: {path}")
        
        if not path.is_file():
            raise ValueError(f"路径不是文件: {path}")
        
        try:
            with open(path, 'r', encoding=encoding) as f:
                return f.read()
        except UnicodeDecodeError:
            # 尝试其他编码
            encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
            for enc in encodings:
                try:
                    with open(path, 'r', encoding=enc) as f:
                        return f.read()
                except UnicodeDecodeError:
                    continue
            raise Exception(f"无法解码文件: {path}")
        except Exception as e:
            raise Exception(f"读取文件失败: {e}")
    
    def write_text_file(self, 
                       file_path: Union[str, Path], 
                       content: str, 
                       encoding: str = 'utf-8') -> None:
        """写入文本文件
        
        Args:
            file_path: 文件路径
            content: 文件内容
            encoding: 文件编码
            
        Raises:
            Exception: 写入失败时抛出异常
        """
        path = Path(file_path)
        
        # 确保目录存在
        path.parent.mkdir(parents=True, exist_ok=True)
        
        try:
            with open(path, 'w', encoding=encoding) as f:
                f.write(content)
        except Exception as e:
            raise Exception(f"写入文件失败: {e}")
    
    def cleanup_temp_files(self) -> None:
        """清理临时文件"""
        for temp_file in self.temp_files:
            try:
                if temp_file.exists():
                    temp_file.unlink()
            except Exception:
                pass  # 忽略清理错误
        
        self.temp_files.clear()
    
    def __del__(self):
        """析构函数，自动清理临时文件"""
        self.cleanup_temp_files()


class EmailContentProcessor:
    """邮件内容处理器"""
    
    def __init__(self):
        """初始化内容处理器"""
        self.file_handler = FileHandler()
    
    def process_content_input(self, 
                            content: Optional[str] = None,
                            content_file: Optional[Union[str, Path]] = None) -> str:
        """处理邮件内容输入
        
        Args:
            content: 直接输入的内容
            content_file: 内容文件路径
            
        Returns:
            处理后的邮件内容
            
        Raises:
            ValueError: 输入参数错误时抛出异常
        """
        if content and content_file:
            raise ValueError("不能同时指定content和content_file")
        
        if not content and not content_file:
            raise ValueError("必须指定content或content_file")
        
        if content:
            return content.strip()
        
        if content_file:
            return self.file_handler.read_text_file(content_file)
        
        return ""
    
    def format_email_content(self, content: str, content_type: str = "plain") -> str:
        """格式化邮件内容
        
        Args:
            content: 原始内容
            content_type: 内容类型 ("plain" 或 "html")
            
        Returns:
            格式化后的内容
        """
        if content_type == "html":
            # 如果是HTML内容，确保基本的HTML结构
            if not content.strip().startswith('<'):
                # 简单文本转HTML
                content = content.replace('\n', '<br>\n')
                content = f"<html><body>{content}</body></html>"
        else:
            # 纯文本内容，确保换行符正确
            content = content.replace('\r\n', '\n').replace('\r', '\n')
        
        return content
    
    def extract_email_addresses(self, text: str) -> List[str]:
        """从文本中提取邮件地址
        
        Args:
            text: 包含邮件地址的文本
            
        Returns:
            邮件地址列表
        """
        import re
        
        pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
        return re.findall(pattern, text)
    
    def validate_content_length(self, content: str, max_length: int = 1000000) -> bool:
        """验证内容长度
        
        Args:
            content: 邮件内容
            max_length: 最大长度（默认1MB）
            
        Returns:
            内容长度是否合法
        """
        return len(content.encode('utf-8')) <= max_length